﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace LogikBug.Collections
{
	public class Hashtable<TKey, TValue> : IHashtable<TKey, TValue>
	{
		#region Fields

		private ReadOnly<TKey> _keysReadonly;
		private ReadOnly<TValue> _valuesReadonly;
		private readonly IList<TKey> _keys;
		private readonly IList<TValue> _values;

		#endregion

		#region Constructors

		public Hashtable()
		{
			_keysReadonly = new ReadOnly<TKey>(Enumerable.Empty<TKey>());
			_valuesReadonly = new ReadOnly<TValue>(Enumerable.Empty<TValue>());
			_keys = new List<TKey>();
			_values = new List<TValue>();
		}

		public Hashtable(IEnumerable<Tuple<TKey, TValue>> collection)
		{
			collection.ThrowIfNull();

			_keys = new List<TKey>(collection.Select(k => k.Item1));
			_values = new List<TValue>(collection.Select(v => v.Item2));
		}

		#endregion

		#region Properties

		protected IList<TKey> Keyslist
		{
			get
			{
				return _keys;
			}
		}

		protected IList<TValue> ValuesList
		{
			get
			{
				return _values;
			}
		}

		public virtual TValue this[TKey key]
		{
			get
			{
				return _values[GetIndex(key)];
			}
			set
			{
				_values[GetIndex(key)] = value;
			}
		}

		public IReadOnly<TKey> Keys
		{
			get
			{
				if (_keysReadonly.Count != _keys.Count)
				{
					_keysReadonly = _keys.ToArray();
				}

				return _keysReadonly;
			}
		}

		public IReadOnly<TValue> Values
		{
			get
			{
				if (_valuesReadonly.Count != _values.Count)
				{
					_valuesReadonly = _values.ToArray();
				}

				return _valuesReadonly;
			}
		}

		#endregion

		#region Methods

		protected virtual int GetIndex(TKey key)
		{
			if (!_keys.Contains(key))
			{
				_keys.Add(key);
				_values.Add(default(TValue));
			}

			return _keys.IndexOf(key);
		}

		public virtual void Add(TKey key, TValue value)
		{
			_keys.Add(key);
			_values.Add(value);
		}

		public virtual void Add(Tuple<TKey, TValue> entry)
		{
			_keys.Add(entry.Item1);
			_values.Add(entry.Item2);
		}

		public virtual bool Has(TKey key)
		{
			return _keys.Contains(key);
		}

		public virtual bool Has(TValue value)
		{
			return _values.Contains(value);
		}

		public virtual TValue GetValueOrDefault(TKey key)
		{
			return _values[GetIndex(key)];
		}

		public virtual void Remove(TKey key)
		{
			_values.RemoveAt(_keys.IndexOf(key));
			_keys.Remove(key);
		}

		public virtual void Clear()
		{
			_keys.Clear();
			_values.Clear();
		}

		public IEnumerator<TValue> GetEnumerator()
		{
			return _values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion
	}
}